/*  build_index.cpp
 * Copyright (C) 2010, Francisco Claude & Patrick Nicholson, all rights reserved.
 *
 * Francisco Claude & Patrick Nicholson <(fclaude|p3nichol)@cs.uwaterloo.ca>
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include<iostream>
#include<fstream>
#include<sstream>
#include<algorithm>
#include<vector>
#include<map>
#include<list>
#include<queue>
#include<stack>
#include<limits>
#include<cassert>

using namespace std;

#include "utils.h"
#include "supowit.h"
#include "untangling.h"

class chainCmp {
  public:
    bool operator()(vector<Point*>*  v1,vector<Point*>*  v2) {
      return v1->at(0)->getX() < v2->at(0)->getX();
    }
};

void freePoints(vector<Point*> points) {
  for(size_t i=0; i<points.size(); i++)
    delete points[i];
}

void dump2(vector<Point*> * v, size_t ch) {
  stringstream ss;
  ss << "uch" << ch << ".dat";
  ofstream out(ss.str().c_str());
  for(size_t i=0; i<v->size(); i++)
    out << v->at(i) << endl;
  out.close();
}

void dump(vector<Point*> * v, size_t ch) {
  stringstream ss;
  ss << "ch" << ch << ".dat";
  ofstream out(ss.str().c_str());
  for(size_t i=0; i<v->size(); i++)
    out << v->at(i) << endl;
  out.close();
}

int main(int argc, char ** argv) {

  if(argc!=3) {
    cout << "usage: " << argv[0] << " <input> <output>" << endl;
    return 0;
  }
  
  ifstream input(argv[1]);
  if(!input.is_open()) {
    cerr << "Error opening file: " << argv[1] << endl;
    return -1;
  }
  
  size_t n1, n2;
  input >> n1 >> n2;
  
  cout << "Descending Set: " << n1 << endl;
  cout << "Ascending Set: " << n2 << endl;
  
  double minx1 = numeric_limits<double>::max(), maxy1 = numeric_limits<double>::min();
  double miny1 = numeric_limits<double>::max(), maxx1 = numeric_limits<double>::min();
  vector<Point*> points1;
  for(size_t i = 0; i < n1; i++) {
    int garbage;
    string dir;
    double x,y;
    input >> garbage >> dir >> x >> y;
    if(x<minx1) minx1=x;
    if(x>maxx1) maxx1=x;
    if(y>maxy1) maxy1=y;
    if(y<miny1) miny1=y;
    points1.push_back(new Point(x,y));
  }
  
  vector<vector<Point*>*> chains1 = supowit(points1);
  size_t chainsIn1 = chains1.size();
    
  double xini = minx1-2*chains1.size()-10;
  double yini = maxy1+chains1.size()+10;
  double xfin = maxx1+chains1.size()+10;
  double yfin = miny1-2*chains1.size()-10;
  
  for(size_t i=0; i<chains1.size(); i++) {
    points1.push_back(new Point(xini+i,yini+i));
    points1.push_back(new Point(xfin+i,yfin+i));
  }
  
  freeChains(chains1);
  chains1 = supowit(points1);
  assert(chains1.size()==chainsIn1);
  
  chainCmp iniCmp;
  sort(chains1.begin(), chains1.end(), iniCmp);

  chains1 = untangle(chains1);

  double minx2 = numeric_limits<double>::max(), maxy2 = numeric_limits<double>::min();
  double miny2 = numeric_limits<double>::max(), maxx2 = numeric_limits<double>::min();
  vector<Point*> points2;
  for(size_t i = 0; i < n2; i++) {
    int garbage;
    string dir;
    double x,y;
    input >> garbage >> dir >> x >> y;
    points2.push_back(new Point(-x,y));
    if(-x<minx2) minx2=-x;
    if(-x>maxx2) maxx2=-x;
    if(y>maxy2) maxy2=y;
    if(y<miny2) miny2=y;
  }
  
  vector<vector<Point*>*> chains2 = supowit(points2);
  size_t chainsIn2 = chains2.size();
    
  xini = minx2-2*chains2.size()-10;
  yini = maxy2+chains2.size()+10;
  xfin = maxx2+chains2.size()+10;
  yfin = miny2-2*chains2.size()-10;
  for(size_t i=0; i<chains2.size(); i++) {
    Point *pf1 = new Point(xini+i,yini+i), *pf2 = new Point(xfin+i,yfin+i);
    points2.push_back(pf1);
    points2.push_back(pf2);
  }
  
  freeChains(chains2);
  chains2 = supowit(points2);
  assert(chains2.size()==chainsIn2);
  
  sort(chains2.begin(), chains2.end(), iniCmp);

  input.close();

  chains2 = untangle(chains2);

  ofstream outfile(argv[2],ios::binary);
  if(!outfile.is_open()) {
    cerr << "Error opening file: " << argv[2] << endl;
    return -2;
  }

  size_t totalPoints = writeChainsDes(chains1, outfile); 
  cout << "Descending chains written: " << totalPoints << endl;
  
  totalPoints += writeChainsDes(chains2, outfile);
  cout << "Ascending chains written: " << totalPoints << endl;
  
  outfile.close();

  cout << "File written." << endl;
  cout << "Total chains: " << chains1.size()+chains2.size() << endl;
  cout << "Total points: " << totalPoints << endl;

  freeChains(chains1);
  freeChains(chains2);
  freePoints(points1);
  freePoints(points2);

  return 0;
}

